<!DOCTYPE html>
<html>
    <head>
        <!-- Notes: This should be open in its original path-->
        <meta charset="utf-8">
        <meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
        <link rel="stylesheet" href="../script/semantic/semantic.min.css">
        <script src="../script/jquery-3.6.0.min.js"></script>
        <script src="../script/semantic/semantic.min.js"></script>
        <script src="../script/utils.js"></script>
        <style>
            .upstreamActions{
                position: absolute;
                top: 0.6em;
                right: 0.6em;
            }

            .upstreamLink{
                max-width: 220px;
                display: inline-block;
                word-break: break-all;
            }

            .upstreamEntry .ui.toggle.checkbox input:checked ~ label::before{
                background-color: #00ca52 !important;
            }

            #activateNewUpstream.ui.toggle.checkbox input:checked ~ label::before{
                background-color: #00ca52 !important;
            }

            #upstreamTable{
                max-height: 480px;
                border-radius: 0.3em;
                padding: 0.3em;
                overflow-y: auto;
            }

            body.darkTheme #upstreamTable{
	            border: 1px solid var(--button_border_color);
            }

            .upstreamEntry.inactive{
                background-color: #f3f3f3 !important;
            }

            .upstreamEntry{
                border-radius: 0.4em !important;
                border: 1px solid rgb(233, 233, 233) !important;
            }

            @media (max-width: 499px) {
                .upstreamActions{
                    position: relative;
                    margin-top: 1em;
                    margin-left: 0.4em;
                    margin-bottom: 0.4em;
                }
            }

            .advanceUpstreamOptions{
                padding: 0.6em;
                background-color: var(--theme_advance); 
                width: 100%;
                border-radius: 0.4em;
            }

            .advanceUpstreamOptions.ui.accordion .content{
                padding: 1em !important;
            }
        </style>
    </head>
    <body>
        <link rel="stylesheet" href="../darktheme.css">
        <script src="../script/darktheme.js"></script>
        <br>
        <div class="ui container">
            <div class="ui small pointing secondary menu">
                <a class="item active narrowpadding" data-tab="upstreamlist">Upstreams</a>
                <a class="item narrowpadding" data-tab="newupstream">Add Upstream</a>
            </div>
            <div class="ui tab basic segment active" data-tab="upstreamlist">
                <!-- A list of current existing upstream on this reverse proxy-->
                <div id="upstreamTable">
                    <div class="ui segment">
                        <a><i class="ui loading spinner icon"></i> Loading</a>
                    </div>
                </div>
                <div class="ui message">
                    <i class="ui blue info circle icon"></i> Weighted random will be used for load-balancing. Set weight to 0 for fallback only.
                 </div>
            </div>
            <div class="ui tab basic segment" data-tab="newupstream">
                <!-- Web Form to create a new upstream -->
                <h4 class="ui header">
                    <i class="green add icon"></i>
                    <div class="content">
                        Add Upstream Server
                        <div class="sub header">Create new load balance or fallback upstream origin</div>
                    </div>
                </h4>
                <p style="margin-bottom: 0.4em;">Target IP address with port</p>
                <div class="ui fluid small input">
                    <input type="text" id="originURL" onchange="cleanProxyTargetValue(this);"><br>
                </div>
                <small>E.g. 192.168.0.101:8000 or example.com</small>
                <br><br>
                <div id="activateNewUpstream" class="ui toggle checkbox" style="display:inline-block;">
                    <input type="checkbox" id="activateNewUpstreamCheckbox" style="margin-top: 0.4em;" checked>
                    <label>Activate<br>
                    <small>Enable this upstream for load balancing</small></label>
                </div><br>
                <div class="ui checkbox" style="margin-top: 1.2em;">
                    <input type="checkbox" id="requireTLS">
                    <label>Require TLS<br>
                        <small>Proxy target require HTTPS connection</small></label>
                </div><br>
                <div class="ui checkbox" style="margin-top: 0.6em;">
                    <input type="checkbox" id="skipTlsVerification">
                     <label>Skip Verification<br>
                        <small>Check this if proxy target is using self signed certificates</small></label>
                </div><br>
                 <div class="ui checkbox" style="margin-top: 0.4em;">
                    <input type="checkbox" id="SkipWebSocketOriginCheck" checked>
                    <label>Skip WebSocket Origin Check<br>
                    <small>Check this to allow cross-origin websocket requests</small></label>
                </div>
                <div class="ui advanceUpstreamOptions advanceoptions accordion" style="margin-top:0.6em;">
                    <div class="title">
                        <i class="dropdown icon"></i>
                        Advanced Options
                    </div>
                    <div class="content">
                        <p>Max Concurrent Connections</p>
                        <div class="ui mini fluid input" style="margin-top: -0.6em;">
                            <input type="number" min="0" id="maxConn" value="0">
                        </div>
                        <small>Set to 0 for automatic</small>
                        <br><br>
                        <p>Response Timeout</p>
                        <div class="ui mini right labeled fluid input" style="margin-top: -0.6em;">
                            <input type="number" min="0" id="respTimeout" value="0">
                            <div class="ui basic label">
                                Seconds
                            </div>
                        </div>
                        <small>Maximum waiting time for server header response, set to 0 for default</small>
                        <br>
                    </div>
                </div>
                
                <br><br>
                <button class="ui basic button" onclick="addNewUpstream();"><i class="ui green circle add icon"></i> Create</button>
            </div>
        </div>
        <br><br><br><br>

        </div>
        <script>
            let origins = [];
            let editingEndpoint = {};
            $('.menu .item').tab();
           

            function initOriginList(){
                $.ajax({
                    url: "/api/proxy/upstream/list",
                    method: "GET",
                    data: {
                        "type":"host",
                        "ep": editingEndpoint.ep
                    },
                    success: function(data){
                        if (data.error != undefined){
                            //This endpoint not exists?
                            alert(data.error);
                            return;
                        }else{
                            $("#upstreamTable").html("");

                            if (data.ActiveOrigins.length == 0){
                                //There is no upstream for this proxy rule
                                $("#upstreamTable").append(`<tr>
                                    <td colspan="2"><b><i class="ui yellow exclamation triangle icon"></i> No Active Upstream Origin</b><br>
                                        This HTTP proxy rule will always return Error 521 when requested. To fix this, add or enable a upstream origin to this proxy endpoint.
                                        <div class="ui divider"></div>
                                    </td>
                                </tr>`);
                            }

                            data.ActiveOrigins.forEach(upstream => {
                                renderUpstreamEntryToTable(upstream, true);
                            });

                            data.InactiveOrigins.forEach(upstream => {
                                renderUpstreamEntryToTable(upstream, false);
                            });

                            $(".advanceUpstreamOptions.accordion").accordion();

                            let totalUpstreams = data.ActiveOrigins.length + data.InactiveOrigins.length;
                            if (totalUpstreams == 1){
                                $(".lowPriorityButton").addClass('disabled');
                            }

                            if (parent && parent.document.getElementById("httpProxyList") != null){
                                //Also update the parent display
                                let element = $(parent.document.getElementById("httpProxyList")).find(".upstreamList.editing");
                                let upstreams = "";
                                data.ActiveOrigins.forEach(upstream => {
                                    //Check if the upstreams require TLS connections
                                    let tlsIcon = "";
                                    if (upstream.RequireTLS){
                                        tlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
                                        if (upstream.SkipCertValidations){
                                            tlsIcon = `<i class="yellow lock icon" title="TLS/SSL mode without verification"></i>`
                                        }
                                    }

                                    let upstreamLink = `${upstream.RequireTLS?"https://":"http://"}${upstream.OriginIpOrDomain}`;

                                    upstreams += `<a href="${upstreamLink}" target="_blank">${upstream.OriginIpOrDomain} ${tlsIcon}</a><br>`;
                                });

                                if (data.ActiveOrigins.length == 0){
                                    upstreams = `<i class="ui yellow exclamation triangle icon"></i> No Active Upstream Origin<br>`
                                }
                                $(element).html(upstreams);
                            }

                            $(".ui.checkbox").checkbox();
                        }
                    }
                })
            }
            
            function renderUpstreamEntryToTable(upstream, isActive){
                function newUID(){return"00000000-0000-4000-8000-000000000000".replace(/0/g,function(){return(0|Math.random()*16).toString(16)})};
                let tlsIcon = "";
                if (upstream.RequireTLS){
                    tlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
                    if (upstream.SkipCertValidations){
                        tlsIcon = `<i class="yellow lock icon" title="TLS/SSL mode without verification"></i>`
                    }
                }

                //Priority Arrows 
                let downArrowClass = "";
                if (upstream.Weight == 0 ){
                    //Cannot go any lower
                    downArrowClass = "disabled";
                }
                let url = `${upstream.RequireTLS?"https://":"http://"}${upstream.OriginIpOrDomain}`
                let payload = encodeURIComponent(JSON.stringify(upstream));
                let domUID = newUID();

                //Timeout values are stored as ms in the backend
                $("#upstreamTable").append(`<div class="ui upstreamEntry ${isActive?"":"inactive"} basic segment" data-domid="${domUID}" data-payload="${payload}" data-priority="${upstream.Priority}">
                    <h4 class="ui header">
                        <div class="ui toggle checkbox" style="display:inline-block;">
                            <input type="checkbox" class="enableState" name="enabled" style="margin-top: 0.4em;" onchange="saveUpstreamUpdate('${domUID}');" ${isActive?"checked":""}>
                            <label></label>
                        </div>
                        <div class="content">
                        <a href="${url}" target="_blank" class="upstreamLink">${upstream.OriginIpOrDomain} ${tlsIcon}</a>
                            <div class="sub header">${isActive?(upstream.Weight==0?"Fallback Only":"Active"):"Inactive"} | Weight: ${upstream.Weight}x </div>
                        </div>
                    </h4>
                    <div class="advanceOptions" style="display:none;">
                        <div class="upstreamOriginField">
                            <p>New upstream origin IP address or domain</p>
                            <div class="ui small fluid input" style="margin-top: -0.6em;">
                                <input type="text" class="newOrigin" value="${upstream.OriginIpOrDomain}" onchange="handleAutoOriginClean('${domUID}');">
                            </div>
                            <small>e.g. 192.168.0.101:8000 or example.com</small>
                        </div>
                        <div class="ui divider"></div>
                        <div class="ui checkbox">
                            <input type="checkbox" class="reqTLSCheckbox" ${upstream.RequireTLS?"checked":""}>
                            <label>Require TLS<br>
                                <small>Proxy target require HTTPS connection</small></label>
                        </div><br>
                        <div class="ui checkbox" style="margin-top: 0.6em;">
                            <input type="checkbox" class="skipVerificationCheckbox" ${upstream.SkipCertValidations?"checked":""}>
                                <label>Skip Verification<br>
                                <small>Check this if proxy target is using self signed certificates</small></label>
                        </div><br>
                            <div class="ui checkbox" style="margin-top: 0.4em;">
                            <input type="checkbox" class="SkipWebSocketOriginCheck" ${upstream.SkipWebSocketOriginCheck?"checked":""}>
                            <label>Skip WebSocket Origin Check<br>
                            <small>Check this to allow cross-origin websocket requests</small></label>
                        </div><br>
                        <!-- Advance Settings -->
                         <div class="ui advanceUpstreamOptions accordion" style="margin-top:0.6em;">
                            <div class="title">
                                <i class="dropdown icon"></i>
                                Advanced Options
                            </div>
                            <div class="content">
                                <p>Max Concurrent Connections</p>
                                <div class="ui mini fluid input" style="margin-top: -0.6em;">
                                    <input type="number" min="0" class="maxConn" value="${upstream.MaxConn}">
                                </div>
                                <small>Set to 0 for default value</small>
                                <br>
                                <p style="margin-top: 0.6em;">Response Timeout</p>
                                <div class="ui mini right labeled fluid input" style="margin-top: -0.6em;">
                                    <input type="number" min="0" class="respTimeout" value="${upstream.RespTimeout/1000}">
                                     <div class="ui basic label">
                                        Seconds
                                    </div>
                                </div>
                                <small>Maximum waiting time before Zoraxy receive server header response, set to 0 for default</small>
                                
                            </div>
                        </div>
                    </div>
                    <div class="upstreamActions">
                        <!-- Change Priority -->
                        <button class="ui basic circular icon button highPriorityButton" title="Increase Weight" onclick="increaseUpstreamWeight('${domUID}');"><i class="ui arrow up icon"></i></button>
                        <button class="ui basic circular icon button lowPriorityButton ${downArrowClass}" title="Reduce Weight"  onclick="decreaseUpstreamWeight('${domUID}');"><i class="ui arrow down icon"></i></button>
                        <button class="ui basic circular icon editbtn button" onclick="handleUpstreamOriginEdit('${domUID}');" title="Edit Upstream Destination"><i class="ui grey edit icon"></i></button>
                        <button style="display:none;" class="ui basic circular icon trashbtn button" onclick="removeUpstream('${domUID}');" title="Remove Upstream"><i class="ui red trash icon"></i></button>
                        <button style="display:none;" class="ui basic circular icon savebtn button" onclick="saveUpstreamUpdate('${domUID}');" title="Save Changes"><i class="ui green save icon"></i></button>
                        <button style="display:none;" class="ui basic circular icon cancelbtn button" onclick="initOriginList();" title="Cancel"><i class="ui grey times icon"></i></button>
                    </div>
                </div>`);
            }

            /* New Upstream Origin Functions */

            //Clearn the proxy target value, make sure user do not enter http:// or https://
            //and auto select TLS checkbox if https:// exists
            function cleanProxyTargetValue(input){
                let targetDomain = $(input).val().trim();
                if (targetDomain.startsWith("http://")){
                    targetDomain = targetDomain.substr(7);
                    $(input).val(targetDomain);
                    $("#requireTLS").parent().checkbox("set unchecked");
                }else if (targetDomain.startsWith("https://")){
                    targetDomain = targetDomain.substr(8);
                    $(input).val(targetDomain);
                    $("#requireTLS").parent().checkbox("set checked");
                }else{
                    //URL does not contains https or http protocol tag
                    //sniff header
                    $.cjax({
                            url: "/api/proxy/tlscheck",
                            method: "POST",
                            data: {url: targetDomain},
                            success: function(data){
                                if (data.error != undefined){

                                }else if (data == "https"){
                                    $("#requireTLS").parent().checkbox("set checked");
                                }else if (data == "http"){
                                    $("#requireTLS").parent().checkbox("set unchecked");
                                }
                            }
                    })
                }
            }

            //Add a new upstream to this http proxy rule
            function addNewUpstream(){
                let origin = $("#originURL").val().trim();
                let requireTLS = $("#requireTLS")[0].checked;
                let skipVerification = $("#skipTlsVerification")[0].checked;
                let skipWebSocketOriginCheck = $("#SkipWebSocketOriginCheck")[0].checked;
                let activateLoadbalancer = $("#activateNewUpstreamCheckbox")[0].checked;
                let maxConn = $("#maxConn").val();
                let respTimeout = $("#respTimeout").val();

                if (maxConn == "" || isNaN(maxConn)){ 
                    maxConn = 0;
                }

                if (respTimeout == "" || isNaN(respTimeout)){
                    respTimeout = 0;
                }

                if (origin == ""){
                    parent.msgbox("Upstream origin cannot be empty", false);
                    return;
                }

                //Convert seconds to ms
                respTimeout = parseInt(respTimeout) * 1000;

                $.cjax({
                    url: "/api/proxy/upstream/add",
                    method: "POST",
                    data:{
                        "ep": editingEndpoint.ep,
                        "origin": origin,
                        "tls": requireTLS,
                        "tlsval": skipVerification,
                        "bpwsorg":skipWebSocketOriginCheck,
                        "active": activateLoadbalancer,
                        "maxconn": maxConn,
                        "respt": respTimeout,
                    },
                    success: function(data){
                        if (data.error != undefined){
                            parent.msgbox(data.error, false);
                        }else{
                            parent.msgbox("New upstream origin added");
                            initOriginList();
                            $("#originURL").val("");
                            $("#maxConn").val("0");
                            $("#respTimeout").val("0");
                        }
                    }
                })
            }

            //Get a upstream setting data from DOM element
            function getUpstreamSettingFromDOM(upstream){
                //Get the original setting from DOM payload
                let originalSettings = $(upstream).attr("data-payload");
                originalSettings = JSON.parse(decodeURIComponent(originalSettings));
                
                //Get the updated settings if any
                let requireTLS = $(upstream).find(".reqTLSCheckbox")[0].checked;
                let skipTLSVerification = $(upstream).find(".skipVerificationCheckbox")[0].checked;
                let skipWebSocketOriginCheck = $(upstream).find(".SkipWebSocketOriginCheck")[0].checked;

                //Advance options
                let maxConn = $(upstream).find(".maxConn").val();
                let respTimeout = $(upstream).find(".respTimeout").val();

                if (maxConn == "" || isNaN(maxConn)){ 
                    maxConn = 0;
                }

                if (respTimeout == "" || isNaN(respTimeout)){
                    respTimeout = 0;
                }

                respTimeout = parseInt(respTimeout) * 1000;

                //Update the original setting with new one just applied
                originalSettings.OriginIpOrDomain = $(upstream).find(".newOrigin").val();
                originalSettings.RequireTLS = requireTLS;
                originalSettings.SkipCertValidations = skipTLSVerification;
                originalSettings.SkipWebSocketOriginCheck = skipWebSocketOriginCheck;
                originalSettings.MaxConn = parseInt(maxConn);
                originalSettings.RespTimeout = respTimeout;

                //console.log(originalSettings);
                return originalSettings;
            }

            //Handle setting change on upstream config
            function saveUpstreamUpdate(upstreamDomID){
                let targetDOM = $(`.upstreamEntry[data-domid=${upstreamDomID}]`);
                let originalSettings = $(targetDOM).attr("data-payload");
                originalSettings = JSON.parse(decodeURIComponent(originalSettings));
                let newConfig = getUpstreamSettingFromDOM(targetDOM);
                let isActive = $(targetDOM).find(".enableState")[0].checked;
                console.log(newConfig);
                $.cjax({
                    url: "/api/proxy/upstream/update",
                    method: "POST",
                    data: {
                        ep: editingEndpoint.ep,
                        origin: originalSettings.OriginIpOrDomain, //Original ip or domain as key
                        payload: JSON.stringify(newConfig),
                        active: isActive,
                    },
                    success: function(data){
                        if (data.error != undefined){
                            parent.msgbox(data.error, false);

                        }else{
                            parent.msgbox("Upstream setting updated");
                            initOriginList();
                        }
                    }
                })
            }

            //Edit the upstream origin of this upstream entry
            function handleUpstreamOriginEdit(upstreamDomID){
                let targetDOM = $(`.upstreamEntry[data-domid=${upstreamDomID}]`);
                let originalSettings = getUpstreamSettingsFromDomID(upstreamDomID);
                let originIP = originalSettings.OriginIpOrDomain;

                //Change the UI to edit mode
                $(".editbtn").hide();
                $(".lowPriorityButton").hide();
                $(".highPriorityButton").hide();
                $(targetDOM).find(".trashbtn").show();
                $(targetDOM).find(".savebtn").show();
                $(targetDOM).find(".cancelbtn").show();
                $(targetDOM).find(".advanceOptions").show();
            }

            //Check if the entered URL contains http or https
            function handleAutoOriginClean(domid){
                let targetDOM = $(`.upstreamEntry[data-domid=${domid}]`);
                let targetTLSCheckbox = $(targetDOM).find(".reqTLSCheckbox");
                let targetDomain = $(targetDOM).find(".newOrigin").val().trim();
                if (targetDomain.startsWith("http://")){
                    targetDomain = targetDomain.substr(7);
                    $(input).val(targetDomain);
                    $(targetTLSCheckbox).parent().checkbox("set unchecked");
                }else if (targetDomain.startsWith("https://")){
                    targetDomain = targetDomain.substr(8);
                    $(input).val(targetDomain);
                    $(targetTLSCheckbox).parent().checkbox("set checked");
                }else{
                    //URL does not contains https or http protocol tag
                    //sniff header
                    $.cjax({
                            url: "/api/proxy/tlscheck",
                            method: "POST",
                            data: {url: targetDomain},
                            success: function(data){
                                if (data.error != undefined){

                                }else if (data == "https"){
                                    $(targetTLSCheckbox).parent().checkbox("set checked");
                                }else if (data == "http"){
                                    $(targetTLSCheckbox).parent().checkbox("set unchecked");
                                }
                            }
                    })
                }
            }

            function getUpstreamSettingsFromDomID(domid){
                let targetDOM = $(`.upstreamEntry[data-domid=${domid}]`);
                if (targetDOM.length == 0){
                    return undefined;
                }
                let upstreamSettings = $(targetDOM).attr("data-payload");
                upstreamSettings = JSON.parse(decodeURIComponent(upstreamSettings));
                return upstreamSettings;
            }

            function increaseUpstreamWeight(domid){
                let upstreamSetting = getUpstreamSettingsFromDomID(domid);
                let originIP = upstreamSetting.OriginIpOrDomain;
                let currentWeight = upstreamSetting.Weight;
                setUpstreamWeight(originIP, currentWeight+1);
            }

            function decreaseUpstreamWeight(domid){
                let upstreamSetting = getUpstreamSettingsFromDomID(domid);
                let originIP = upstreamSetting.OriginIpOrDomain;
                let currentWeight = upstreamSetting.Weight;
                setUpstreamWeight(originIP, currentWeight-1);
            }

            //Set a weight of a upstream
            function setUpstreamWeight(originIP, newWeight){
                $.cjax({
                    url: "/api/proxy/upstream/setPriority",
                    method: "POST",
                    data: {
                        ep: editingEndpoint.ep,
                        origin: originIP,
                        weight: newWeight,
                    },
                    success: function(data){
                        if (data.error != undefined){
                            parent.msgbox(data.error, false);
                        }else{
                            parent.msgbox("Upstream Weight Updated");
                            initOriginList();
                        }
                    }
                })
            }

            //Handle removal of an upstream
            function removeUpstream(domid){
                let targetDOM = $(`.upstreamEntry[data-domid=${domid}]`);
                let originalSettings = $(targetDOM).attr("data-payload");
                originalSettings = JSON.parse(decodeURIComponent(originalSettings));
                let UpstreamKey = originalSettings.OriginIpOrDomain;
                if (!confirm("Confirm removing " + UpstreamKey + " from upstream?")){
                    return;
                }
                //Remove the upstream
                $.cjax({
                    url: "/api/proxy/upstream/remove",
                    method: "POST",
                    data: {
                        ep: editingEndpoint.ep,
                        origin: originalSettings.OriginIpOrDomain, //Original ip or domain as key
                    },
                    success: function(data){
                        if (data.error != undefined){
                            parent.msgbox(data.error, false);

                        }else{
                            parent.msgbox("Upstream deleted");
                            initOriginList();
                        }
                    }
                })
            }

            if (window.location.hash.length > 1){
                let payloadHash = window.location.hash.substr(1);
                try{
                    payloadHash = JSON.parse(decodeURIComponent(payloadHash));
                    $(".epname").text(payloadHash.ep);
                    editingEndpoint = payloadHash;
                    initOriginList();
                }catch(ex){
                    console.log("Unable to load endpoint data from hash")
                }
            }

            function closeThisWrapper(){
                parent.hideSideWrapper(true);
            }
        </script>
    </body>
</html>